use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::string::String;
use crate::builder::Builder;
use crate::database::TableField;
use crate::transform::{db_to_class, mysql_to_java_type};
use crate::utils::underline_to_hump;

#[derive(Debug, Copy, Clone)]
enum IdType {
    Auto,
    None,
    Input,
    AssignId,
    AssignUUID,
    Unknown
}

fn get_primary_key(types: IdType) -> String {
    match types {
        IdType::Auto => String::from("IdType.AUTO"),
        IdType::None => String::from("IdType.NONE"),
        IdType::Input => String::from("IdType.INPUT"),
        IdType::AssignId => String::from("IdType.ASSIGN_ID"),
        IdType::AssignUUID => String::from("IdType.ASSIGN_UUID"),
        IdType::Unknown => String::from("")
    }
}

impl From<&str> for IdType {
    fn from(item: &str) -> Self {
        match item {
            "IdType.AUTO" => IdType::Auto,
            "IdType.NONE" => IdType::None,
            "IdType.INPUT" => IdType::Input,
            "IdType.ASSIGN_ID" => IdType::AssignId,
            "IdType.ASSIGN_UUID" => IdType::AssignUUID,
            _ => IdType::Unknown
        }
    }
}



// 实体构建
#[derive(Debug)]
pub struct EntityBuilder {
    // 注释
    notes: String,
    // 基础路径
    base_path: String,
    // 实体路径
    entity_path: String,
    // 实体名
    entity_name: String,
    // 主键方式
    primary_key_type: IdType,
    // 是否增加lombok注解
    use_lombok: bool,
    // 属性列表
    fields: Vec<TableField>,
    // 时间格式
    time_format: Option<String>,
    // 时区配置
    time_zone: Option<String>
}

impl EntityBuilder {

    pub fn new(base_path: &str, notes: &str, entity_path: &str, entity_name: &str, fields: Vec<TableField>) -> Self {
        EntityBuilder {
            base_path: base_path.to_string(),
            notes: notes.to_string(),
            entity_path: entity_path.to_string(),
            entity_name: entity_name.to_string(),
            primary_key_type: IdType::Auto,
            use_lombok: true,
            fields,
            time_format: Some("yyyy-MM-dd HH:mm:ss".to_string()),
            time_zone: Some("GMT+8".to_string())
        }
    }

    fn import_build(&self) -> String {
        let mut time_types:Vec<String> = Vec::new();
        let mut result = String::from("");
        result.push_str(format!("package {};\n\n", self.entity_path).as_str());
        result.push_str(format!("import com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\n").as_str());
        if self.time_format.is_some() {
            result.push_str("import com.fasterxml.jackson.annotation.JsonFormat;\n");
        }
        if self.use_lombok {
            result.push_str("import lombok.Data;\nimport lombok.experimental.Accessors;\nimport lombok.NoArgsConstructor;\nimport lombok.AllArgsConstructor;\n");
        }
        result.push_str("import java.io.Serializable;\n");

        self.fields.clone().into_iter().for_each(|node| {
            if node.field_type == "date" || node.field_type == "datetime" || node.field_type == "timestamp" {
                if !time_types.contains(&node.field_type) {
                    time_types.push(node.field_type);
                }
            }
        });

        time_types.into_iter().for_each(|node| {
            db_to_class(&node).map(|types| {
                result.push_str(format!("import {};\n", types).as_str())
            });
        });

        result.push_str(self.notes.as_str());
        if self.use_lombok {
            result.push_str(format!("@Data\n@Accessors(chain = true)\n@AllArgsConstructor\n@NoArgsConstructor\n").as_str());
        }

        result.to_string()

    }

    pub fn field_build(&self) -> String {
        let mut result = String::from("");
        result.push_str(format!("public class {} implements Serializable {{\n\n\tprivate static final long serialVersionUID = 1L;\n\n", self.entity_name).as_str());

        self.fields.iter().for_each(|node| {

            if node.field_comment.is_some() {
                result.push_str(format!("\t/**\n\t * {}\n\t */\n", node.field_comment.clone().unwrap()).as_str())
            }
            let type_name = mysql_to_java_type(node.field_type.as_str());
            let field_name = underline_to_hump(node.field_name.clone(), false);

            if node.field_key.eq("PRI") {
                result.push_str(format!(
                    "\t@TableId(value = \"{}\", type = {})\n\tprivate {} {};\n\n",
                    node.field_name,
                    get_primary_key(self.primary_key_type),
                    type_name.unwrap(),
                    field_name
                ).as_str());
            } else {
                result.push_str(format!("\tprivate {} {};\n\n", type_name.unwrap(), field_name).as_str());
            }
        });
        result.push_str("}");

        result.to_string()
    }

}

impl Builder for EntityBuilder {

    // 构建
    fn build(&self) -> String {
        let mut result = String::from("");
        result.push_str(self.import_build().as_str());
        result.push_str(self.field_build().as_str());
        result.to_string()
    }

    fn build_file(&self) {
        let parent_path = format!("{}/{}", self.base_path, self.entity_name);
        fs::create_dir_all(Path::new(parent_path.as_str())).unwrap();
        let file_name = format!("{}/{}.java", parent_path, self.entity_name);
        // println!("{}", file_name);
        let mut file = File::create(file_name).unwrap();
        file.write_all(self.build().as_bytes()).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_id() {
        let id_type = IdType::from("IdType.AUTO");
        print!("{:?}", id_type);
        let x: IdType = "IdType.AUTO".into();
        print!("{:?}", x);
    }

}